#define _GNU_SOURCE  // cfsetspeed 函数不是posix 默认函数 所以需要定义这个宏 引入扩展方法
#include "app_serial.h"
#include  "log/log.h"
#include  <stdlib.h>  // malloc
#include  "app_bluetooth.h"
#include  <unistd.h> // sleep
#include  "app_device.h"
 

//串口设备初始化
SerialDevice *app_serial_device_Init(char *filename )
{
    //先h声明一个device 然后再进行扩展
    Device *device=app_device_init(filename);
    if(!device){
        log_error("device_init error");
        return NULL;
    }
    //由于串口设备是device的子类 所以需要使用realloc扩展一下
    SerialDevice *serialDevice =  realloc(device,sizeof(SerialDevice));
    if(!serialDevice){
        log_error("realloc error");
         goto SERIAL_DEVICE_FAILED;
    }

    //设置参数 
    if(make_raw(serialDevice)<0){
        log_error("make_raw error");
        goto SET_DEVICE_FAILED;
    }


    if(app_serial_SetBraudRate(serialDevice,BR_115200)<0)
    {
        log_error("app_serial_SetBraudRate error");
        goto SET_DEVICE_FAILED;
    }
    if( app_serial_SetParity(serialDevice,Parity_None)<0)
    {
        log_error("app_serial_SetParity error");
        goto SET_DEVICE_FAILED;
    }
    if( app_serial_SetStopBit(serialDevice,StopBit_1)<0)
    {
        log_error("app_serial_SetStopBit error");
        goto SET_DEVICE_FAILED;
    }
    tcflush(serialDevice->super.fd, TCIOFLUSH);
    return serialDevice;

SET_DEVICE_FAILED:
      destroy_device(device); 

SERIAL_DEVICE_FAILED:
       return NULL;

}

/**
 * 设置波特率
 * 
 */
int app_serial_SetBraudRate(SerialDevice *serialDevice,BraudRate br){

    serialDevice->br=br;

    // 1获取串口属性 
    struct termios termios_attrs; //创建一个结构体
    if(tcgetattr(serialDevice->super.fd,&termios_attrs)<0){
        log_error("tcgetattr SetBraudRate error");
        return -1;  
    }

    // 2 修改属性（波特率）
    // cfsetispeed(&termios_attrs,br);
    // cfsetospeed(&termios_attrs,br);
    cfsetspeed(&termios_attrs,br);
    // 3  设置串口属性 // 不要立刻生效 最好等到全部设置完毕后 统一生效  使用flush控制  // TCSANOW 理解生效有一致性问题  TCSADRAIN  这个是等待数据写入完成自动生效 TCSAFLUSH 手动控制
    tcsetattr(serialDevice->super.fd,TCSAFLUSH,&termios_attrs);
   
    return 0;
 

}

/**
 * 设置奇偶校验
 * 
  */

int app_serial_SetParity(SerialDevice *serialDevice,Parity parity){
    serialDevice->parity=parity;


    // 1获取串口属性 
    struct termios termios_attrs; //创建一个结构体
    if(tcgetattr(serialDevice->super.fd,&termios_attrs)<0){
        log_error("tcgetattr  SetParity error");
        return -1;  
    }

    // 2 修改属性（奇偶校验）
    termios_attrs.c_cflag &= ~(PARENB|PARODD);// 清除校验位  //本质上就是把 启用位和奇偶位 都置为0
    termios_attrs.c_cflag |= parity; //设置校验位  启用位和奇偶位  置parity
 
 
    
    // 3  设置串口属性 // 不要立刻生效 最好等到全部设置完毕后 统一生效  使用flush控制  // TCSANOW 理解生效有一致性问题  TCSADRAIN  这个是等待数据写入完成自动生效 TCSAFLUSH 手动控制
    tcsetattr(serialDevice->super.fd,TCSAFLUSH,&termios_attrs);
   
    return 0;


}

/**
 * 设置停止位
 * 
 */
int app_serial_SetStopBit(SerialDevice *serialDevice,StopBit stopBit){
    serialDevice->stopBit=stopBit;

        struct termios termios_attrs; //创建一个结构体
    if(tcgetattr(serialDevice->super.fd,&termios_attrs)<0){
        log_error("tcgetattr StopBit error");
        return -1;  
    }

    // 2 修改属性（奇偶校验）
    termios_attrs.c_cflag &= ~CSTOP;// 清除停止位  //本质上就是把 停止位 都置为0
    termios_attrs.c_cflag |= stopBit; //设置停止位  停止位 设置参数值
 
 
    
    // 3  设置串口属性 // 不要立刻生效 最好等到全部设置完毕后 统一生效  使用flush控制  // TCSANOW 理解生效有一致性问题  TCSADRAIN  这个是等待数据写入完成自动生效 TCSAFLUSH 手动控制
    tcsetattr(serialDevice->super.fd,TCSAFLUSH,&termios_attrs);
   
    return 0;
}

static int set_block_mode(SerialDevice *serialDevice,int block)
{
 

     struct termios termios_attrs; //创建一个结构体
    if(tcgetattr(serialDevice->super.fd,&termios_attrs)<0){
        log_error("tcgetattr StopBit error");
        return -1;  
    }

     if(block){ //阻塞模式
        termios_attrs.c_cc[VTIME] = 0; // 没有读到数据会一直等待
        termios_attrs.c_cc[VMIN] = 1; // 至少读取一个字符才返回
     }else{//非阻塞模式
       termios_attrs.c_cc[VTIME] = 2; // 单位为100ms 等待时间为0.2秒
        termios_attrs.c_cc[VMIN] = 0;
     }
 
 
 
    
    // 3  设置串口属性 // 不要立刻生效 最好等到全部设置完毕后 统一生效  使用flush控制  // TCSANOW 理解生效有一致性问题  TCSADRAIN  这个是等待数据写入完成自动生效 TCSAFLUSH 手动控制
    tcsetattr(serialDevice->super.fd,TCSAFLUSH,&termios_attrs);
   
    return 0;
} 


/**
 * 设置连接类型  //尝试用9600去连接 与蓝牙相连  
 */
int app_serial_SetConnectionType(SerialDevice *serialDevice,ConnectType connectionType)
{
  serialDevice->super.conn_type=connectionType;

  app_serial_SetBraudRate(serialDevice,BR_9600);
  tcflush(serialDevice->super.fd,TCIOFLUSH);
  //  默认的阻塞模式 会导致 连不上的情况会卡死在这里，所以测试前将串口暂时设置为非阻塞模式
  set_block_mode(serialDevice,0);
  if(app_bluetooth_status(&serialDevice->super)==0)  // 如果连接成功 说明是首次连接 进行初始化
  { 
      // 初始化蓝牙相关属性
      app_bluetooth_rename(serialDevice,"atguigu");
      app_bluetooth_setBraudRate(serialDevice,BR_115200);
      app_bluetooth_setNetId(serialDevice,"1234");  //??
      app_bluetooth_setMAddr(serialDevice,"0002");
      app_bluetooth_reset(serialDevice);
      //   等待蓝牙重启
     sleep(1); 
  }

  //  连接失败 说明不是首次连接 直接改为115200  
   app_serial_SetBraudRate(serialDevice,BR_115200);
   tcflush(serialDevice->super.fd,TCIOFLUSH);
   //再次测试连接
   if(app_bluetooth_status(&serialDevice->super)==0){
      log_error("bluetooth connect failed");
      return -1;
   }

   set_block_mode(serialDevice->super.fd,1);
   tcflush(serialDevice->super.fd,TCIOFLUSH);
   return 0;
}

static  int make_raw(SerialDevice *serialDevice)
{
    
     struct termios termios_attrs; //创建一个结构体
    if(tcgetattr(serialDevice->super.fd,&termios_attrs)<0){
        log_error("tcgetattr make_raw error");
        return -1;  
    }

    // 2 修改属性 重置为原始a状态
    cfmakeraw(&termios_attrs);
    
    
    // 3  设置串口属性 // 不要立刻生效 最好等到全部设置完毕后 统一生效  使用flush控制  // TCSANOW 理解生效有一致性问题  TCSADRAIN  这个是等待数据写入完成自动生效 TCSAFLUSH 手动控制
    tcsetattr(serialDevice->super.fd,TCSAFLUSH,&termios_attrs);
   
    return 0;
}